﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "GLRenderState.h"
#include "GLStateManager.h"

namespace UX_STL
{
namespace GLShell
{

	/*static CMetaClass	_s_cMetaRenderState,
						_s_cMetaColorBuffer,
						_s_cMetaStencilState,
						_s_cMetaStencilStateSide,
						_s_cMetaDepthState,
						_s_cMetaStates;


	_CREATE_META_OBJECT( GLRenderState,						_s_cMetaRenderState );
	_CREATE_META_OBJECT( GLRenderState::TColorBuffer,			_s_cMetaColorBuffer );
	_CREATE_META_OBJECT( GLRenderState::TStencilState,			_s_cMetaStencilState );
	_CREATE_META_OBJECT( GLRenderState::TStencilState::TSide,	_s_cMetaStencilStateSide );
	_CREATE_META_OBJECT( GLRenderState::TDepthState,			_s_cMetaDepthState );
	_CREATE_META_OBJECT( GLRenderState::TStates,				_s_cMetaStates );*/


	inline bool ValidateBlendFunc(gl_blend_func::type &eType)
	{
		switch ( eType )
		{
			case gl_blend_func::ZERO :
			case gl_blend_func::ONE :
			case gl_blend_func::SRC_COLOR :
			case gl_blend_func::ONE_MINUS_SRC_COLOR :
			case gl_blend_func::DST_COLOR :
			case gl_blend_func::ONE_MINUS_DST_COLOR :
			case gl_blend_func::SRC_ALPHA :
			case gl_blend_func::ONE_MINUS_SRC_ALPHA :
			case gl_blend_func::DST_ALPHA :
			case gl_blend_func::ONE_MINUS_DST_ALPHA :
			case gl_blend_func::CONST_COLOR :
			case gl_blend_func::ONE_MINUS_CONST_COLOR :
			case gl_blend_func::CONST_ALPHA :
			case gl_blend_func::ONE_MINUS_CONST_ALPHA :
			case gl_blend_func::SRC_ALPHA_SATURATE :
			case gl_blend_func::SRC1_COLOR :
			//case gl_blend_func::SRC1_ALPHA :
			case gl_blend_func::ONE_MINUS_SRC1_COLOR :
			case gl_blend_func::ONE_MINUS_SRC1_ALPHA :
				return true;
		};
		eType = gl_blend_func::_INIT;
		return false;
	}

	inline bool ValidateBlendEquation(gl_blend_equation::type &eType)
	{
		switch ( eType )
		{
			case gl_blend_equation::ADD :
			case gl_blend_equation::SUB :
			case gl_blend_equation::REV_SUB :
			case gl_blend_equation::MIN :
			case gl_blend_equation::MAX :
				return true;
		};
		eType = gl_blend_equation::_INIT;
		return false;
	}

	inline bool ValidateCompareMode(gl_compare::type &eType)
	{
		switch ( eType )
		{
			case gl_compare::NEVER :
			case gl_compare::LESS :
			case gl_compare::EQUAL :
			case gl_compare::LEQUAL :
			case gl_compare::GREATER :
			case gl_compare::NOTEQUAL :
			case gl_compare::GEQUAL :
			case gl_compare::ALWAYS :
				return true;
		};
		eType = gl_compare::_INIT;
		return false;
	}

	inline bool ValidateStencilOp(gl_stencil_op::type &eType)
	{
		switch ( eType )
		{
			case gl_stencil_op::DECR :
			case gl_stencil_op::DECR_WRAP :
			case gl_stencil_op::INCR :
			case gl_stencil_op::INCR_WRAP :
			case gl_stencil_op::INVERT :
			case gl_stencil_op::KEEP :
			case gl_stencil_op::REPLACE :
			case gl_stencil_op::ZERO :
				return true;
		};
		eType = gl_stencil_op::_INIT;
		return false;
	}



//------------------------ GLRenderState --------------------------//
/*
=================================================
	установить состояния рендера
=================================================
*/
	void GLRenderState::Apply() const
	{
		const uint	u_max_buffers	= C_GL_MAX_COLOR_ATTACHMENTS;

		// Color //
		FOR( i, aColorBuffers )
		{
			const uint				u_buf	= aColorBuffers[i].uIndex;
			const TColorBuffer &	cb		= aColorBuffers[i];

			if ( u_buf >= u_max_buffers )
				continue;
			
			SMAN()->ColorMaski( u_buf, cb.eMask );
			SMAN()->BlendState( u_buf, cb.bBlending );

			if ( cb.bBlending )
			{
				SMAN()->BlendEquationSeparatei( u_buf, cb.eModeRGB, cb.eModeAlpha );
				SMAN()->BlendFuncSeparatei( u_buf, cb.eSrcRGB, cb.eSrcAlpha, cb.eDstRGB, cb.eDstAlpha );
			}
		}


		// Culling //
		if ( eCullMode != e_cull_mode::NONE )
		{
			SMAN()->SetState( e_gl_state::CULL_FACE, EnumNoCmp(eCullMode, e_cull_mode::_CULL_MASK) );
			
			if ( EnumCmpE( eCullMode, e_cull_mode::BACK ) )
				SMAN()->CullFace( gl_face::BACK );
			else
			if ( EnumCmpE( eCullMode, e_cull_mode::FRONT ) )
				SMAN()->CullFace( gl_face::FRONT );
			else
			if ( EnumCmpE( eCullMode, e_cull_mode::FRONT_AND_BACK ) )
				SMAN()->CullFace( gl_face::FRONT_AND_BACK );

			if ( EnumCmpE( eCullMode, e_cull_mode::FACE_CCW ) )
				SMAN()->FrontFace( gl_front_face::CCW );
			else
				SMAN()->FrontFace( gl_front_face::CW );
		}
		else
			SMAN()->Disable( e_gl_state::CULL_FACE );


		// Depth //
		SMAN()->DepthFunc( sDepth.eFunc );
		SMAN()->DepthMask( sDepth.bWrite );
		SMAN()->SetState( e_gl_state::DEPTH_TEST, sDepth.bTest );


		// Stencil //
		if ( sStencil.bTest )
		{
			const TStencilState::TSide &	front	= sStencil.aSide[0];
			const TStencilState::TSide &	back	= sStencil.aSide[1];

			SMAN()->Enable( e_gl_state::STENCIL_TEST );
			SMAN()->StencilFuncSeparate( gl_face::BACK, back.eFunc, back.uFuncRef, back.uFuncMask );
			SMAN()->StencilFuncSeparate( gl_face::FRONT, front.eFunc, front.uFuncRef, front.uFuncMask );
			SMAN()->StencilMaskSeparate( gl_face::BACK, back.uMask );
			SMAN()->StencilMaskSeparate( gl_face::FRONT, front.uMask );
			SMAN()->StencilOpSeparate( gl_face::BACK, back.eSFail, back.eDFail, back.eDPPass );
			SMAN()->StencilOpSeparate( gl_face::FRONT, front.eSFail, front.eDFail, front.eDPPass );
		}
		else
			SMAN()->Disable( e_gl_state::STENCIL_TEST );

		// States //
		SMAN()->PolygonMode( ePolygon );
		
		switch ( ePolygon )
		{
			case gl_polygon_mode::FILL :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_FILL,	sStates.bPolygonOffset );
				SMAN()->SetState( e_gl_state::POLYGON_SMOOTH,		sStates.bSmooth );
				break;

			case gl_polygon_mode::LINE :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_LINE,	sStates.bPolygonOffset );
				SMAN()->SetState( e_gl_state::LINE_SMOOTH,			sStates.bSmooth );
				break;

			case gl_polygon_mode::POINT :
				SMAN()->SetState( e_gl_state::POLYGON_OFFSET_POINT,	sStates.bPolygonOffset );
				break;
		};

		if ( sStates.bPolygonOffset )
			GL_CALL( glPolygonOffset( vPolygonOffset.x, vPolygonOffset.y ) );
		
		SMAN()->SetState( e_gl_state::DEPTH_CLAMP,					sStates.bDepthClamp );
		SMAN()->SetState( e_gl_state::DITHER,						sStates.bDither );
		SMAN()->SetState( e_gl_state::PROGRAM_POINT_SIZE,			sStates.bProgramPointSize );
		SMAN()->SetState( e_gl_state::TEXTURE_CUBE_MAP_SEAMLESS,		sStates.bCubemapSeamless );
		SMAN()->SetState( e_gl_state::FRAMEBUFFER_SRGB,				sStates.bFramebufferSRGB );
	}
	
/*
=================================================
	установка некорректных значений
=================================================
*/
	void GLRenderState::Invalidate()
	{
	}
	
/*
=================================================
	испавление некорректных значений
=================================================
*/
	void GLRenderState::Validate()
	{
		const uint	u_max_buffers	= C_GL_MAX_COLOR_ATTACHMENTS;

		FOR( i, aColorBuffers )
		{
			const uint		u_buf	= aColorBuffers[i].uIndex;
			 TColorBuffer &	cb		= aColorBuffers[i];

			if ( u_buf >= u_max_buffers )
				continue;

			if ( !ValidateBlendFunc( cb.eSrcRGB ) )			cb.eSrcRGB		= gl_blend_func::ONE;
			if ( !ValidateBlendFunc( cb.eSrcAlpha ) )		cb.eSrcAlpha	= cb.eSrcRGB;
			if ( !ValidateBlendFunc( cb.eDstRGB ) )			cb.eDstRGB		= gl_blend_func::ZERO;
			if ( !ValidateBlendFunc( cb.eDstAlpha ) )		cb.eDstAlpha	= cb.eDstAlpha;

			if ( !ValidateBlendEquation( cb.eModeRGB ) )	cb.eModeRGB		= gl_blend_equation::ADD;
			if ( !ValidateBlendEquation( cb.eModeAlpha ) )	cb.eModeAlpha	= cb.eModeRGB;
		}

		if ( !ValidateCompareMode( sDepth.eFunc ) )			sDepth.eFunc	= gl_compare::LEQUAL;

		if ( !ValidateCompareMode( sStencil.aSide[0].eFunc ) )
			sStencil.aSide[0].eFunc	= gl_compare::ALWAYS;

		if ( !ValidateCompareMode( sStencil.aSide[1].eFunc ) )
			sStencil.aSide[1].eFunc	= gl_compare::ALWAYS;

		ValidateStencilOp( sStencil.aSide[0].eSFail );
		ValidateStencilOp( sStencil.aSide[0].eDFail );
		ValidateStencilOp( sStencil.aSide[0].eDPPass );
		ValidateStencilOp( sStencil.aSide[1].eSFail );
		ValidateStencilOp( sStencil.aSide[1].eDFail );
		ValidateStencilOp( sStencil.aSide[1].eDPPass );
	}
	
/*
=================================================
	возвращает итератор
=================================================
*
	CMetaSetIterator * GLRenderState::s_GetIterator(const GLRenderState &sRS)
	{
		if ( _s_cMetaRenderState.IsInit() )
			return _s_cMetaRenderState.GetSetIter( &sRS );
		else
			return null;
	}
	
/*
=================================================
	инициализировать мета данные
=================================================
*
	void GLRenderState::s_InitMetaData()
	{
		CMetaAddIterator *	iter = null;

		if ( !_s_cMetaColorBuffer.IsInit() )
		{
			TColorBuffer	s_cb;

			_s_cMetaColorBuffer.Create( "ColorBuffer", sizeof(s_cb) );

			iter = _s_cMetaColorBuffer.GetAddIter( &s_cb );
			iter->AddField( &s_cb.uIndex,		"index" );
			iter->AddField( &s_cb.eMask,			"mask" );
			iter->AddField( &s_cb.bBlending,		"blending" );
			iter->AddField( &s_cb.eSrcRGB,		"src_rgb" );
			iter->AddField( &s_cb.eSrcAlpha,		"src_alpha" );
			iter->AddField( &s_cb.eDstRGB,		"dst_rgb" );
			iter->AddField( &s_cb.eDstAlpha,		"dst_alpha" );
			iter->AddField( &s_cb.eModeRGB,		"mode_rgb" );
			iter->AddField( &s_cb.eModeAlpha,	"mode_alpha" );
			iter->Free();
		}

		if ( !_s_cMetaStencilStateSide.IsInit() )
		{
			TStencilState::TSide	s_side;

			_s_cMetaStencilStateSide.Create( "StencilStateSide", sizeof(s_side) );

			iter = _s_cMetaStencilStateSide.GetAddIter( &s_side );
			iter->AddField( &s_side.eSFail,		"sfail" );
			iter->AddField( &s_side.eDFail,		"dfail" );
			iter->AddField( &s_side.eDPPass,		"dppass" );
			iter->AddField( &s_side.eFunc,		"func" );
			iter->AddField( &s_side.uFuncRef,	"func_ref" );
			iter->AddField( &s_side.uFuncMask,	"func_mask" );
			iter->AddField( &s_side.uMask,		"mask" );
			iter->Free();
		}

		if ( !_s_cMetaStencilState.IsInit() )
		{
			TStencilState	s_state;

			_s_cMetaStencilState.Create( "StencilState", sizeof(s_state) );

			iter = _s_cMetaStencilState.GetAddIter( &s_state );
			iter->AddField( &s_state.aSide[0],	"front" );
			iter->AddField( &s_state.aSide[1],	"back" );
			iter->AddField( &s_state.bTest,		"test" );
			iter->Free();
		}

		if ( !_s_cMetaDepthState.IsInit() )
		{
			TDepthState	s_state;

			_s_cMetaDepthState.Create( "DepthState", sizeof(s_state) );

			iter = _s_cMetaDepthState.GetAddIter( &s_state );
			iter->AddField( &s_state.eFunc,		"func" );
			iter->AddField( &s_state.bWrite,		"write" );
			iter->AddField( &s_state.bTest,		"test" );
			iter->Free();
		}

		if ( !_s_cMetaStates.IsInit() )
		{
			TStates		s_states;

			_s_cMetaStates.Create( "GLStates", sizeof(s_states) );

			iter = _s_cMetaStates.GetAddIter( &s_states );
			iter->AddField( &s_states.bCubemapSeamless,		"cubemap_seamless" );
			iter->AddField( &s_states.bDepthClamp,			"depth_clamp" );
			iter->AddField( &s_states.bDither,				"dither" );
			iter->AddField( &s_states.bPolygonOffset,		"polygon_offset" );
			iter->AddField( &s_states.bProgramPointSize,		"program_point_size" );
			iter->AddField( &s_states.bSmooth,				"smooth" );
			iter->AddField( &s_states.bFramebufferSRGB,		"fbo_srgb" );
		}

		if ( !_s_cMetaRenderState.IsInit() )
		{
			GLRenderState	s_rs;
		
			_s_cMetaRenderState.Create( "RenderState", sizeof(s_rs) );

			iter = _s_cMetaRenderState.GetAddIter( &s_rs );
			iter->AddField( &s_rs.aColorBuffers[0],	"color_buffer_0" );
			iter->AddField( &s_rs.aColorBuffers[1],	"color_buffer_1" );
			iter->AddField( &s_rs.aColorBuffers[2],	"color_buffer_2" );
			iter->AddField( &s_rs.aColorBuffers[3],	"color_buffer_3" );
			iter->AddField( &s_rs.aColorBuffers[4],	"color_buffer_4" );
			iter->AddField( &s_rs.aColorBuffers[5],	"color_buffer_5" );
			iter->AddField( &s_rs.aColorBuffers[6],	"color_buffer_6" );
			iter->AddField( &s_rs.aColorBuffers[7],	"color_buffer_7" );
			iter->AddField( &s_rs.sStencil,			"stencil" );
			iter->AddField( &s_rs.sDepth,			"depth" );
			iter->AddField( &s_rs.sStates,			"states" );
			iter->AddField( &s_rs.vPolygonOffset,	"polygon_offset" );
			iter->AddField(	&s_rs.eCullMode,		"cull_mode" );
			iter->AddField( &s_rs.ePolygon,			"polygon_mode" );
			iter->Free();
		}
	}
	*/
//-------------------------------------------------------------------

}	// GLShell
}	// UX_STL